/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is MPEG4IP.
 *
 * The Initial Developer of the Original Code is Cisco Systems Inc.
 * Portions created by Cisco Systems Inc. are
 * Copyright (C) Cisco Systems Inc. 2001.  All Rights Reserved.
 *
 * See ETSI TS 102 366 V1.2.1 Annex F for how to put Ac3 in MP4.
 *
 * Contributor(s):
 *      Edward Groenendaal      egroenen@cisco.com
 */

#include "src/impl.h"

namespace mp4v2
{
    namespace impl
    {

        ///////////////////////////////////////////////////////////////////////////////


        MP4DAc3Atom::MP4DAc3Atom(MP4File &file)
            : MP4Atom(file, "dac3")
        {
            AddProperty( new MP4BitfieldProperty(*this, "fscod", 2)); /* 0 */
            AddProperty( new MP4BitfieldProperty(*this, "bsid", 5)); /* 1 */
            AddProperty( new MP4BitfieldProperty(*this, "bsmod", 3)); /* 2 */
            AddProperty( new MP4BitfieldProperty(*this, "acmod", 3)); /* 3 */
            AddProperty( new MP4BitfieldProperty(*this, "lfeon", 1)); /* 4 */
            AddProperty( new MP4BitfieldProperty(*this, "bit_rate_code", 5)); /* 5 */
            AddProperty( new MP4BitfieldProperty(*this, "reserved", 5)); /* 6 */
            m_pProperties[6]->SetReadOnly(true);
        }

        void MP4DAc3Atom::Generate()
        {
            MP4Atom::Generate();


        }

        /*
         * Normalling the Dump of the atom looks like the following, we want to spruce
         * this up a bit to give more information.
         *
         *      type = dac3
         *       fscod = 0 (0x0) <2 bits>
         *       bsid = 8 (0x08) <5 bits>
         *       bsmod = 0 (0x0) <3 bits>
         *       acmod = 7 (0x7) <3 bits>
         *       lfeon = 1 (0x1) <1 bits>
         *       bit_rate_code = 15 (0x0f) <5 bits>
         *       reserved = 0 (0x00) <5 bits>
         *
         * into:
         *      type = dac3
         *       fscod = 0 (0x0) <2 bits> [48 kHz]
         *       bsid = 8 (0x08) <5 bits>
         *       bsmod = 0 (0x0) <3 bits> [Main audio service: complete main (CM)]
         *       acmod = 7 (0x7) <3 bits> [3/2 (L, C, R, SL, SR)]
         *       lfeon = 1 (0x1) <1 bits> [ENABLED]
         *       bit_rate_code = 15 (0x0f) <5 bits> [448 kbit/s]
         *       reserved = 0 (0x00) <5 bits>
         *
         */
        void MP4DAc3Atom::Dump(uint8_t indent, bool dumpImplicits)
        {

            MP4BitfieldProperty *fscodProp = ((MP4BitfieldProperty *)m_pProperties[0]);
            MP4BitfieldProperty *bsidProp = ((MP4BitfieldProperty *)m_pProperties[1]);
            MP4BitfieldProperty *bsmodProp = ((MP4BitfieldProperty *)m_pProperties[2]);
            MP4BitfieldProperty *acmodProp = ((MP4BitfieldProperty *)m_pProperties[3]);
            MP4BitfieldProperty *lfeonProp = ((MP4BitfieldProperty *)m_pProperties[4]);
            MP4BitfieldProperty *brcProp = ((MP4BitfieldProperty *)m_pProperties[5]);
            MP4BitfieldProperty *resProp = ((MP4BitfieldProperty *)m_pProperties[6]);

            log.dump(indent++, MP4_LOG_VERBOSE2, "\"%s\": type = dac3",
                     GetFile().GetFilename().c_str() );

            if (fscodProp)
            {
                uint64_t fscod = 0xFF;
                const char *fscodString;
                const char *fscods[] =
                {
                    "48", "44.1", "32", "Reserved",
                };

                fscod = fscodProp->GetValue();

                if (fscod < (sizeof(fscods) / sizeof(fscods[0])))
                {
                    fscodString = fscods[fscod];
                }
                else
                {
                    fscodString = "Invalid value";
                }

                uint8_t hexWidth = fscodProp->GetNumBits() / 4;
                if (hexWidth == 0 || (fscodProp->GetNumBits() % 4))
                {
                    hexWidth++;
                }

                log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": fscod = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits> [%s kHz]",
                         GetFile().GetFilename().c_str(),
                         fscod, (int)hexWidth, fscod, fscodProp->GetNumBits(), fscodString);
            }
            if (bsidProp)  bsidProp->Dump(indent, dumpImplicits);

            if (bsmodProp)
            {
                uint64_t bsmod = 0xFF;
                const char *bsmodString;
                const char *bsmods[] =
                {
                    "Main audio service: complete main (CM)",
                    "Main audio srrvice: music and effects (ME)",
                    "Associated service: visually impaired (VI)",
                    "Associated service: hearing impaired (HI)",
                    "Associated service: dialogue (D)",
                    "Associated service: commentary (C)",
                    "Associated service: emergency (E)",
                    "Associated service: voice over (VO) or Main audio service: karaoke",
                };

                bsmod = bsmodProp->GetValue();

                if (bsmod < (sizeof(bsmods) / sizeof(bsmods[0])))
                {
                    bsmodString = bsmods[bsmod];
                }
                else
                {
                    bsmodString = "Invalid value";
                }

                uint8_t hexWidth = bsmodProp->GetNumBits() / 4;
                if (hexWidth == 0 || (bsmodProp->GetNumBits() % 4))
                {
                    hexWidth++;
                }

                log.dump(indent, MP4_LOG_VERBOSE2,
                         "\"%s\": bsmod = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits> [%s]",
                         GetFile().GetFilename().c_str(),
                         bsmod, (int)hexWidth, bsmod, bsmodProp->GetNumBits(), bsmodString);
            }

            if (acmodProp)
            {
                uint64_t acmod = 0xFF;
                const char *acmodString;

                const char *acmods[] =
                {
                    "1 + 1 (Ch1, Ch2)",
                    "1/0 (C)",
                    "2/0 (L, R)",
                    "3/0 (L, C, R)",
                    "2/1 (L, R, S)",
                    "3/1 (L, C, R, S)",
                    "2/2 (L, R, SL, SR)",
                    "3/2 (L, C, R, SL, SR)",
                };

                acmod = acmodProp->GetValue();

                if (acmod < (sizeof(acmods) / sizeof(acmods[0])))
                {
                    acmodString = acmods[acmod];
                }
                else
                {
                    acmodString = "Invalid value";
                }

                uint8_t hexWidth = acmodProp->GetNumBits() / 4;
                if (hexWidth == 0 || (acmodProp->GetNumBits() % 4))
                {
                    hexWidth++;
                }

                log.dump(indent, MP4_LOG_VERBOSE2,
                         "\"%s\": acmod = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits> [%s]",
                         GetFile().GetFilename().c_str(),
                         acmod, (int)hexWidth, acmod, acmodProp->GetNumBits(), acmodString);
            }

            if (lfeonProp)
            {
                uint64_t lfeon = lfeonProp->GetValue();
                uint8_t hexWidth = lfeonProp->GetNumBits() / 4;

                if (hexWidth == 0 || (lfeonProp->GetNumBits() % 4))
                {
                    hexWidth++;
                }

                log.dump(indent, MP4_LOG_VERBOSE2,
                         "\"%s\": lfeon = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits> [%s]",
                         GetFile().GetFilename().c_str(), lfeon, (int)hexWidth, lfeon,
                         lfeonProp->GetNumBits(), lfeon ? "ENABLED" : "DISABLED");
            }

            if (brcProp)
            {
                uint32_t bit_rate_codes[] =
                {
                    32,
                    40,
                    48,
                    56,
                    64,
                    80,
                    96,
                    112,
                    128,
                    160,
                    192,
                    224,
                    256,
                    320,
                    384,
                    448,
                    512,
                    576,
                    640,
                };
                uint64_t bit_rate_code = brcProp->GetValue();
                uint32_t bit_rate;

                if (bit_rate_code < (sizeof(bit_rate_codes) / sizeof(bit_rate_codes[0])))
                {
                    bit_rate = bit_rate_codes[bit_rate_code];
                }
                else
                {
                    bit_rate = 0;
                }

                uint8_t hexWidth = brcProp->GetNumBits() / 4;
                if (hexWidth == 0 || (brcProp->GetNumBits() % 4))
                {
                    hexWidth++;
                }

                log.dump(indent, MP4_LOG_VERBOSE2,
                         "\"%s\": bit_rate_code = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits> [%" PRIu32 " kbit/s]",
                         GetFile().GetFilename().c_str(),
                         bit_rate_code, (int)hexWidth, bit_rate_code,
                         brcProp->GetNumBits(), bit_rate);
            }
            if (resProp) resProp->Dump(indent, dumpImplicits);
        }

        ///////////////////////////////////////////////////////////////////////////////

    }
} // namespace mp4v2::impl
